package de.projectgoodmorning.alarm.activities;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;

import android.app.Activity;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import de.projectgoodmorning.alarm.AlarmController;
import de.projectgoodmorning.alarm.BTController;
import de.projectgoodmorning.alarm.R;
import de.projectgoodmorning.alarm.activities.util.Actionbar;
import de.projectgoodmorning.alarm.activities.util.AlarmNotificationManager;
import de.projectgoodmorning.alarm.dialogs.ChooseBtDeviceDialog;
import de.projectgoodmorning.alarm.dialogs.ConfirmDialog;
import de.projectgoodmorning.alarm.dialogs.OnBtDeviceChoosenListener;
import de.projectgoodmorning.alarm.model.gateway.CombinedAlarm;
import de.projectgoodmorning.alarm.model.generic.Alarm;
import de.projectgoodmorning.alarm.storage.AlarmStorage;
import de.projectgoodmorning.alarm.util.RsrcHelper;

/**
 * Die MainActivity Klasse ist die Hauptklasse der gesamten Anwendung.
 * In dieser Activity werden die gespeicherten Alarme angezeigt.
 * Des Weiteren wird dessen Status als auch Name und Uhrzeit angzeigt.
 * 
 * Von hier aus koennen Alarme aktiviert, geloescht und bearbeitet werden.
 * Wobei das Bearbeiten explizit in der ManageAlarmActivity vorgenommen wird.
 * 
 * Von der MainActivity gelangt man in die Einstellungen. Ausserdem kann hier
 * die Verbindung zu einem Bluetooth-Geraet hergestellt werden.
 */
public class MainActivity extends Activity {
	private static final String TAG = MainActivity.class.getName();	

	/** Key fuer Intent-Extras: ID des Alarms, welcher bearbeitet wird */
	public static final String MANAGED_ALARM_ID = "manage_alarm";

	/** Key fuer Intent-Extras: Legt fest, dass ein neuer Alarm erstellt werden soll */
	public static final String NEW_ALARM = "new_alarm";

	private static final int MANAGE_ACTIVITY = 100;
	private static final int BT_DEVICES_DIALOG = 10;
	private static final int BT_ACTIVE_REQUEST = 20;

	// Controller
	private AlarmController alarmController;
	private BTController btController;
	private AlarmStorage alarmStorage;
	
	private AlarmNotificationManager notificationManager;

	// View components
	private Actionbar action;
	private ListView list;
	private TextView btInfo;
	
	private CombinedAlarm[] alarms;

	@Override
	public void onCreate( Bundle savedInstanceState ) {
		super.onCreate( savedInstanceState );
		setContentView( R.layout.activity_main );
		setTitle( R.string.title_activity_main_acitivity );

		list = ( ListView ) findViewById( R.id.alarmList );
		btInfo = ( TextView ) findViewById( R.id.btInfoTextView );
		
		initActionbar();

		startService( new Intent( this, BTController.class ) );
		bindService( new Intent( this, BTController.class ), 
				btServiceConnection, Context.BIND_AUTO_CREATE );

		startService( new Intent( this, AlarmController.class ) );
		bindService( new Intent( this, AlarmController.class ), 
				alarmControllerServiceConnection, Context.BIND_AUTO_CREATE );
		
		alarmStorage = new AlarmStorage( this );
		notificationManager = new AlarmNotificationManager( this );
		
		createList();
		setBtInfo();
		
		AlarmController.setOnExecuteListener( new Runnable() {
			@Override public void run() {
				// Remove Alarm from notification bar (if already rings)
				CombinedAlarm[] alarms = alarmStorage.toArray();
				for( CombinedAlarm a : alarms ) {
					if( a.getState() == Alarm.INACTIVE ) {
						notificationManager.deactiveNotification( a );
					}
				}
 				
				createList();
			}
		} );
	}

	@Override
	public boolean onContextItemSelected( MenuItem item ) {
		final AdapterView.AdapterContextMenuInfo info = 
				( AdapterView.AdapterContextMenuInfo ) item.getMenuInfo();
		int menuItemIndex = item.getItemId();

		final CombinedAlarm alarm = alarms[info.position];
		
		// activate
		if( menuItemIndex == 0 ) {
			changeState( alarm );
			Log.d( TAG, "State changed - Alarm-ID: " + alarm.id );
		}
		
		// edit
		if( menuItemIndex == 1 ) {
			startManageAlarm( alarm.id );
			Log.d( TAG, "Change alarm data started - Alarm-ID: " + alarm.id );
		}
		
		// delete
		if( menuItemIndex == 2 ) {
			ConfirmDialog dialog = new ConfirmDialog( this );
			dialog.setOnConfirmClicked( new DialogInterface.OnClickListener() {
				@Override
				public void onClick( DialogInterface dialog, int which ) {
					Log.d( TAG, "Alarm removed choosen - Alarm-ID: " + alarm.id );
					
					if( alarm.getState() == Alarm.ACTIVE ) {
						changeState( alarm );
					}
					
					alarmStorage.removeById( alarm.id );
					createList();
				}
			} );
			
			dialog.setOnCancelClicked( new DialogInterface.OnClickListener() {
				@Override
				public void onClick( DialogInterface dialog, int which ) {
					dialog.cancel();
				}
			} );
			
			dialog.show();
		}

		return super.onContextItemSelected( item );
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		createList();
	}
	
	@Override
	public void onCreateContextMenu( ContextMenu menu, View v, ContextMenuInfo menuInfo ) {
		super.onCreateContextMenu( menu, v, menuInfo );
		String[] menuItems = getResources().getStringArray( R.array.context_menue );
		for( int i = 0; i < menuItems.length; i++ ) {
			menu.add( Menu.NONE, i, i, menuItems[i] );
		}
	}

	@Override
	protected void onActivityResult( int requestCode, int resultCode, Intent data ) {
		super.onActivityResult( requestCode, resultCode, data );
		if( requestCode == MANAGE_ACTIVITY && resultCode == RESULT_OK ) {
			createList();
		} else {
			alarmStorage.loadAlarms();
			createList();
		}
		
		if( requestCode == BT_ACTIVE_REQUEST && resultCode == RESULT_OK ) {
			showDialog( BT_DEVICES_DIALOG );
		} else if( requestCode == BT_ACTIVE_REQUEST && resultCode == RESULT_CANCELED ) {
			showToast( RsrcHelper.getString( R.string.cannotActiveBt, this ) );
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if( alarmController != null ) {
			unbindService( alarmControllerServiceConnection );
		}
		
		if( btController != null ) {
			unbindService( btServiceConnection );
		}
	}

	@Override
	public boolean onCreateOptionsMenu( Menu menu ) {
		getMenuInflater().inflate( R.menu.activity_main, menu );
		return true;
	}
	
	@Override
	protected Dialog onCreateDialog( int id ) {
		switch( id ) {
		case BT_DEVICES_DIALOG:
			return new ChooseBtDeviceDialog( this, new OnBtDeviceChoosenListener() {
				@Override
				public void btDeviceChoosen( BluetoothDevice device ) {
					try {
						btController.connect( device.getAddress() );
					} catch( Exception e ) {
						Log.i( TAG, e.getMessage() );
						showToast( RsrcHelper.getString( 
								R.string.msg_noConnectionEstablished, 
								getApplicationContext() ) );
					}
					
					setBtInfo();
				}
			} );
		}
		return null;
	}

	@Override
	public boolean onOptionsItemSelected( MenuItem item ) {
		if( item.getItemId() == R.id.menu_bt_settings ) {
			showBtSettings();
		}
		
		if( item.getItemId() == R.id.menu_app_settings ) {
			startSettings();
		}
		
		return super.onOptionsItemSelected( item );
	}
	
	/////////// Helpers /////////////////////////////////////
	private void showBtSettings() {
		if( btController.isEnabled() ) {
			showDialog( BT_DEVICES_DIALOG );
		} else {
			Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
			startActivityForResult( enableBtIntent, BT_ACTIVE_REQUEST );
		}
	}
	
	private void startSettings() {
		startActivity( new Intent( this, SettingsActivity.class ) );
	}
	
	private void startAddNewAlarm() {
		Log.d( TAG, "add new alarm selected" );
		
		Intent i = new Intent( MainActivity.this, ManageAlarmActivity.class );
		i.putExtra( NEW_ALARM, "" );
		startActivityForResult( i, MANAGE_ACTIVITY );
	}
	
	private void createList() {
		alarms = alarmStorage.toArray();
		Arrays.sort( alarms, new Comparator<CombinedAlarm>() {
			@Override
			public int compare( CombinedAlarm lhs, CombinedAlarm rhs ) {
				Calendar tempCal = Calendar.getInstance();
				Calendar lhsCal = Calendar.getInstance();
				Calendar rhsCal = Calendar.getInstance();

				tempCal.setTimeInMillis( lhs.getMainAlarm().getMoment() );
				lhsCal.set( Calendar.HOUR_OF_DAY, tempCal.get( Calendar.HOUR_OF_DAY ) );
				lhsCal.set( Calendar.MINUTE, tempCal.get( Calendar.MINUTE ) );
				
				tempCal.setTimeInMillis( rhs.getMainAlarm().getMoment() );
				rhsCal.set( Calendar.HOUR_OF_DAY, tempCal.get( Calendar.HOUR_OF_DAY ) );
				rhsCal.set( Calendar.MINUTE, tempCal.get( Calendar.MINUTE ) );
				
				if( lhsCal.getTimeInMillis() < rhsCal.getTimeInMillis() )
		            return -1;
		        if( lhsCal.getTimeInMillis() > rhsCal.getTimeInMillis()  )
		            return 1;
		            
		        return 0;
			}
		} );
		
		list.setAdapter( new ArrayAdapter<CombinedAlarm>( 
				this, R.layout.alarm_list_item, alarms ) {
			
			@Override
			public View getView( int position, View convertView, ViewGroup parent ) {
				LayoutInflater inflater = MainActivity.this.getLayoutInflater();
				View row = inflater.inflate( R.layout.alarm_list_item, parent, false );
				
				ImageView state = ( ImageView ) row.findViewById( R.id.stateImageView );
				ImageView arrow = ( ImageView ) row.findViewById( R.id.arrorImageView );
				TextView time = ( TextView ) row.findViewById( R.id.timeTextView );
				TextView name = ( TextView ) row.findViewById( R.id.nameTextView );
				
				final CombinedAlarm a = alarms[position];

				arrow.setOnClickListener( new OnClickListener() {
					@Override
					public void onClick( View v ) {
						startManageAlarm( a.id );
					}
				} );		

				if( a.getState() == Alarm.ACTIVE ) {
					state.setImageResource( R.drawable.active );
				} else {
					state.setImageResource( R.drawable.inactive );
				}
			
				SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm", Locale.getDefault() );
				
				String clock = RsrcHelper.getString( R.string.clock, getApplicationContext() );
				String s = sdf.format( new Date( a.getMainAlarm().getMoment() ) ) + " " + clock;
				time.setText( s );
			
				name.setText( a.name );
				
				return row; 
			}
		} );
		
		list.setOnCreateContextMenuListener( this );
		list.setOnItemClickListener( new OnItemClickListener() {
			@Override
			public void onItemClick( AdapterView<?> arg0, View view, int pos, long arg3 ) {
				changeState( alarms[pos] );
			}
		} );
	}
	
	private void changeState( CombinedAlarm alarm ) {
		int state = alarm.getState();
		if( state == Alarm.INACTIVE ) {
			alarmController.addAlarms( alarm.exportAll() );
			notificationManager.activateNotification( alarm );
		} else {
			alarmController.removeAlarms( alarm.exportAll() );
			notificationManager.deactiveNotification( alarm );
		}
		
		list.invalidateViews();
	}
	
	private void startManageAlarm( int id ) { 
		CombinedAlarm ca = alarmStorage.getById( id );
		
		if( ca.getState() == Alarm.INACTIVE ) {
			Intent i = new Intent( MainActivity.this, ManageAlarmActivity.class );
			i.putExtra( MANAGED_ALARM_ID, id );
			startActivityForResult( i, MANAGE_ACTIVITY );
		} else {
			showToast( RsrcHelper.getString( R.string.msg_activeAlarmsNotEditable, this ) );
		}
	}
	
	private void initActionbar() {
		action = new Actionbar( this );
		
		action.setOnAddClicked( new OnClickListener() {
			@Override
			public void onClick( View v ) {
				startAddNewAlarm();
			}
		} );
		
		action.setOnBluetoothClicked( new OnClickListener() {
			@Override
			public void onClick( View v ) {
				showBtSettings();
			}
		} );
		
		action.setOnSettingsClicked( new OnClickListener() {
			@Override
			public void onClick( View v ) {
				startSettings();
			}
		} );
		
	}
	
	private void showToast( String message ) {
		Toast.makeText( this, message, Toast.LENGTH_LONG ).show();
	}
	
	private void setBtInfo() {
		if( btController != null ) {
			if( btController.isConnected() ) {
				btInfo.setText( RsrcHelper.getString( R.string.msg_connectedWith, this )
						+ " " + btController.getConnectedDevice().getName() );
			} else {
				btInfo.setText( RsrcHelper.getString( R.string.msg_noConnectionToBtDevice, this ) );
			}
			
			if( !btController.isEnabled() ) {
				btInfo.setText( "" );
			}
		}
	}
	
	/////////////////////////////////////////////////////////
	
	
	/////////// SERVICE /////////////////////////////////////
	
	private ServiceConnection alarmControllerServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected( ComponentName name ) {
			alarmController = null;
			Log.d( TAG, "alarmController disconnected" );
		}
		@Override
		public void onServiceConnected( ComponentName name, IBinder service ) {
			alarmController = ( ( AlarmController.LocalBinder ) service ).getService();
			
			if( alarmController != null ) {
				Log.d( TAG, "alarmController connected" );
			} else {
				Log.w( TAG, "alarmController could not connected" );
			}
		}
	};
	
	private ServiceConnection btServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected( ComponentName name ) {
			btController = null;
			Log.d( TAG, "BTController disconnected" );
		}
		@Override
		public void onServiceConnected( ComponentName name, IBinder service ) {
			btController = ( ( BTController.LocalBinder ) service ).getService();
			
			if( btController != null ) {
				Log.d( TAG, "BTController connected" );
			} else {
				Log.w( TAG, "BTController could not connected" );
			}
		}
	};
}
